<template>
    <div :status="status" class="uploader-file">
        <slot
            :average-speed="averageSpeed"
            :current-speed="currentSpeed"
            :error="error"
            :extension="extension"
            :file="file"
            :file-category="fileCategory"
            :formated-average-speed="formatedAverageSpeed"
            :formated-size="formatedSize"
            :formated-time-remaining="formatedTimeRemaining"
            :is-complete="isComplete"
            :is-uploading="isUploading"
            :list="list"
            :paused="paused"
            :progress="progress"
            :progress-style="progressStyle"
            :progressing-class="progressingClass"
            :response="response"
            :size="size"
            :status="status"
            :time-remaining="timeRemaining"
            :type="type"
            :uploaded-size="uploadedSize"
        >
            <div :class="progressingClass" :style="progressStyle" class="uploader-file-progress"></div>
            <div class="uploader-file-info">
                <div class="uploader-file-name"><i :icon="fileCategory" class="uploader-file-icon"></i>{{ file.name }}
                </div>
                <div class="uploader-file-size">{{ formatedSize }}</div>
                <div class="uploader-file-meta"></div>
                <div class="uploader-file-status">
                    <span v-show="status !== 'uploading'">{{ statusText }}</span>
                    <span v-show="status === 'uploading'">
            <span>{{ progressStyle.progress }}</span>
            <em>{{ formatedAverageSpeed }}</em>
            <i>{{ formatedTimeRemaining }}</i>
          </span>
                </div>
                <div class="uploader-file-actions">
                    <span @click="pause" class="uploader-file-pause"></span>
                    <span @click="resume" class="uploader-file-resume">️</span>
                    <span @click="retry" class="uploader-file-retry"></span>
                    <span @click="remove" class="uploader-file-remove"></span>
                </div>
            </div>
        </slot>
    </div>
</template>

<script>
import Uploader from '../../simple-uploader/uploader'
import events from '../common/file-events'
import {secondsToStr} from '../common/utils'

const COMPONENT_NAME = 'uploader-file'

export default {
    name: COMPONENT_NAME,
    props: {
        file: {
            type: Object,
            default() {
                return {}
            }
        },
        list: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            response: null,
            paused: false,
            error: false,
            averageSpeed: 0,
            currentSpeed: 0,
            isComplete: false,
            isUploading: false,
            size: 0,
            formatedSize: '',
            uploadedSize: 0,
            progress: 0,
            timeRemaining: 0,
            type: '',
            extension: '',
            progressingClass: ''
        }
    },
    computed: {
        fileCategory() {
            const extension = this.extension
            const isFolder = this.file.isFolder
            let type = isFolder ? 'folder' : 'unknown'
            const categoryMap = this.file.uploader.opts.categoryMap
            const typeMap = categoryMap || {
                image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
                video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
                audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
                document: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
            }
            Object.keys(typeMap).forEach((_type) => {
                const extensions = typeMap[_type]
                if (extensions.indexOf(extension) > -1) {
                    type = _type
                }
            })
            return type
        },
        progressStyle() {
            const progress = Math.floor(this.progress * 100)
            const style = `translateX(${Math.floor(progress - 100)}%)`
            return {
                progress: `${progress}%`,
                webkitTransform: style,
                mozTransform: style,
                msTransform: style,
                transform: style
            }
        },
        formatedAverageSpeed() {
            return `${Uploader.utils.formatSize(this.averageSpeed)} / s`
        },
        status() {
            const isUploading = this.isUploading
            const isComplete = this.isComplete
            const isError = this.error
            const paused = this.paused
            if (isComplete) {
                return 'success'
            } else if (isError) {
                return 'error'
            } else if (isUploading) {
                return 'uploading'
            } else if (paused) {
                return 'paused'
            } else {
                return 'waiting'
            }
        },
        statusText() {
            const status = this.status
            const fileStatusText = this.file.uploader.fileStatusText
            let txt = status
            if (typeof fileStatusText === 'function') {
                txt = fileStatusText(status, this.response)
            } else {
                txt = fileStatusText[status]
            }
            return txt || status
        },
        formatedTimeRemaining() {
            const timeRemaining = this.timeRemaining
            const file = this.file
            if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
                return ''
            }
            let parsedTimeRemaining = secondsToStr(timeRemaining)
            const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
            if (parseTimeRemaining) {
                parsedTimeRemaining = parseTimeRemaining(timeRemaining, parsedTimeRemaining)
            }
            return parsedTimeRemaining
        }
    },
    watch: {
        status(newStatus, oldStatus) {
            if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
                this.tid = setTimeout(() => {
                    this.progressingClass = 'uploader-file-progressing'
                }, 200)
            } else {
                clearTimeout(this.tid)
                this.progressingClass = ''
            }
        }
    },
    methods: {
        _actionCheck() {
            this.paused = this.file.paused
            this.error = this.file.error
            this.isUploading = this.file.isUploading()
        },
        pause() {
            this.file.pause()
            this._actionCheck()
            this._fileProgress()
        },
        resume() {
            this.file.resume()
            this._actionCheck()
        },
        remove() {
            this.file.cancel()
        },
        retry() {
            this.file.retry()
            this._actionCheck()
        },
        processResponse(message) {
            let res = message
            try {
                res = JSON.parse(message)
            } catch (e) {
            }
            this.response = res
        },
        fileEventsHandler(event, args) {
            const rootFile = args[0]
            const file = args[1]
            const target = this.list ? rootFile : file
            if (this.file === target) {
                if (this.list && event === 'fileSuccess') {
                    this.processResponse(args[2])
                    return
                }
                this[`_${event}`].apply(this, args)
            }
        },
        _fileProgress() {
            this.progress = this.file.progress()
            this.averageSpeed = this.file.averageSpeed
            this.currentSpeed = this.file.currentSpeed
            this.timeRemaining = this.file.timeRemaining()
            this.uploadedSize = this.file.sizeUploaded()
            this._actionCheck()
        },
        _fileSuccess(rootFile, file, message) {
            if (rootFile) {
                this.processResponse(message)
            }
            this._fileProgress()
            this.error = false
            this.isComplete = true
            this.isUploading = false
        },
        _fileComplete() {
            this._fileSuccess()
        },
        _fileError(rootFile, file, message) {
            this._fileProgress()
            this.processResponse(message)
            this.error = true
            this.isComplete = false
            this.isUploading = false
        }
    },
    mounted() {
        const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed']
        const fnProps = [
            'isComplete',
            'isUploading',
            {
                key: 'size',
                fn: 'getSize'
            },
            {
                key: 'formatedSize',
                fn: 'getFormatSize'
            },
            {
                key: 'uploadedSize',
                fn: 'sizeUploaded'
            },
            'progress',
            'timeRemaining',
            {
                key: 'type',
                fn: 'getType'
            },
            {
                key: 'extension',
                fn: 'getExtension'
            }
        ]
        staticProps.forEach(prop => {
            this[prop] = this.file[prop]
        })
        fnProps.forEach((fnProp) => {
            if (typeof fnProp === 'string') {
                this[fnProp] = this.file[fnProp]()
            } else {
                this[fnProp.key] = this.file[fnProp.fn]()
            }
        })

        const handlers = this._handlers = {}
        const eventHandler = (event) => {
            handlers[event] = (...args) => {
                this.fileEventsHandler(event, args)
            }
            return handlers[event]
        }
        events.forEach((event) => {
            this.file.uploader.on(event, eventHandler(event))
        })
    },
    destroyed() {
        events.forEach((event) => {
            this.file.uploader.off(event, this._handlers[event])
        })
        this._handlers = null
    }
}
</script>

<style>
.uploader-file {
    position: relative;
    height: 49px;
    line-height: 49px;
    overflow: hidden;
    border-bottom: 1px solid #cdcdcd;
}

.uploader-file[status="waiting"] .uploader-file-pause,
.uploader-file[status="uploading"] .uploader-file-pause {
    display: block;
}

.uploader-file[status="paused"] .uploader-file-resume {
    display: block;
}

.uploader-file[status="error"] .uploader-file-retry {
    display: block;
}

.uploader-file[status="success"] .uploader-file-remove {
    display: none;
}

.uploader-file[status="error"] .uploader-file-progress {
    background: #ffe0e0;
}

.uploader-file-progress {
    position: absolute;
    width: 100%;
    height: 100%;
    background: #e2eeff;
    transform: translateX(-100%);
}

.uploader-file-progressing {
    transition: all .4s linear;
}

.uploader-file-info {
    position: relative;
    z-index: 1;
    height: 100%;
    overflow: hidden;
}

.uploader-file-info:hover {
    background-color: rgba(240, 240, 240, 0.2);
}

.uploader-file-info i,
.uploader-file-info em {
    font-style: normal;
}

.uploader-file-name,
.uploader-file-size,
.uploader-file-meta,
.uploader-file-status,
.uploader-file-actions {
    float: left;
    position: relative;
    height: 100%;
}

.uploader-file-name {
    width: 45%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-indent: 14px;
}

.uploader-file-icon {
    width: 24px;
    height: 24px;
    display: inline-block;
    vertical-align: top;
    margin-top: 13px;
    margin-right: 8px;
}

.uploader-file-icon::before {
    content: "📃";
    display: block;
    height: 100%;
    font-size: 24px;
    line-height: 1;
    text-indent: 0;
}

.uploader-file-icon[icon="folder"]::before {
    content: "📂";
}

.uploader-file-icon[icon="image"]::before {
    content: "📊";
}

.uploader-file-icon[icon="video"]::before {
    content: "📹";
}

.uploader-file-icon[icon="audio"]::before {
    content: "🎵";
}

.uploader-file-icon[icon="document"]::before {
    content: "📋";
}

.uploader-file-size {
    width: 13%;
    text-indent: 10px;
}

.uploader-file-meta {
    width: 8%;
}

.uploader-file-status {
    width: 24%;
    text-indent: 20px;
}

.uploader-file-actions {
    width: 10%;
}

.uploader-file-actions > span {
    display: none;
    float: left;
    width: 16px;
    height: 16px;
    margin-top: 16px;
    margin-right: 10px;
    cursor: pointer;
    background: url("") no-repeat 0 0;
}

.uploader-file-actions > span:hover {
    background-position-x: -21px;
}

.uploader-file-actions .uploader-file-pause {
    background-position-y: 0;
}

.uploader-file-actions .uploader-file-resume {
    background-position-y: -17px;
}

.uploader-file-actions .uploader-file-retry {
    background-position-y: -53px;
}

.uploader-file-actions .uploader-file-remove {
    display: block;
    background-position-y: -34px;
}
</style>
